/*
 * Copyright 2008 Les Hazlewood
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.pojodm.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @author Les Hazlewood
 */
public abstract class DateTimeUtils {

    public static boolean lte( Date lhs, Date rhs ) {
        return lhs.before( rhs ) || lhs.equals( rhs );
    }

    public static boolean gte( Date lhs, Date rhs ) {
        return lhs.after( rhs ) || lhs.equals( rhs );
    }

    public static Date newDate( int year, int month, int day ) {
        return newDate( year, month, day, 0, 0, 0, 0 );
    }

    public static Date newDate( int year, int month, int day, int hour ) {
        return newDate( year, month, day, hour, 0, 0, 0 );
    }

    public static Date newDate( int year, int month, int day, int hour, int minute ) {
        return newDate( year, month, day, hour, minute, 0, 0 );
    }

    public static Date newDate( int year, int month, int day, int hour, int minute, int second ) {
        return newDate( year, month, day, hour, minute, second, 0 );
    }

    public static Date newDate( int year, int month, int day, int hour, int minute, int second, int millis ) {
        Calendar cal = Calendar.getInstance();
        cal.set( Calendar.YEAR, year );
        cal.set( Calendar.MONTH, month );
        cal.set( Calendar.DAY_OF_MONTH, day );
        cal.set( Calendar.HOUR_OF_DAY, hour );
        cal.set( Calendar.MINUTE, minute );
        cal.set( Calendar.SECOND, second );
        cal.set( Calendar.MILLISECOND, millis );
        return cal.getTime();
    }

    public static Date midnight( int year, int month, int day ) {
        return newDate( year, month, day, 0, 0, 0, 0 );
    }

    public static Date today() {
        return midnight();
    }

    /**
     * Returns today's date at midnight, i.e. midnight of the day when this method is executed, which is
     * the earliest possible time for the day.
     * @return
     */
    public static Date midnight() {
        Calendar cal = Calendar.getInstance();
        return midnight( cal ).getTime();
    }

    public static Date midnight( Date in ) {
        if ( in == null ) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime( in );
        return midnight( cal ).getTime();
    }

    /**
     * Normalizes the given argument to midnight on its current year/month/day.  Does not create a new object
     * instance - merely modifies the argument and returns that same argument for method chaining.
     * @param cal the input Calendar to normalize to midnight on that year/month/day.
     * @return the same Calendar instance, normalized to midnight on its year/month/day.
     */
    public static Calendar midnight( Calendar cal ) {
        cal.set( Calendar.HOUR, 0 );
        cal.set( Calendar.MINUTE, 0 );
        cal.set( Calendar.SECOND, 0 );
        cal.set( Calendar.MILLISECOND, 0 );
        return cal;
    }

    /**
     * Returns today's date (i.e. the date at the time this method is called) at the latest possible time
     * before tomorrow (i.e. midnight of the next day).  This is essentially today's date (at the time of
     * invocation) at 23:59:59.999
     * @return
     */
    public static Date latestDayMillis() {
        Calendar cal = Calendar.getInstance();
        return latestDayMillis( cal.getTime() );
    }

    public static Date latestDayMillis( Date in ) {
        if ( in == null ) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime( in );
        return latestDayMillis( cal ).getTime();
    }

    public static Date latestTommorowMillis(){
    	Calendar cal = new GregorianCalendar();
    	cal.roll(Calendar.DATE,true);
    	return latestDayMillis(cal).getTime();
    }

    /**
     * Normalizes the given argument to the latest possible time on its year/month/day.  That is, the very last
     * millisecond of that day, effectively yyyy-mm-dd 23:59:59.999 (since one more millisecond would be
     * considered the earliest possible point of the next day). This method does does not create a new object
     * instance - merely modifies the argument and returns that same argument for method chaining.
     * @param cal the input Calendar to normalize to the latest millisecond on that year/month/day.
     * @return the same Calendar instance, normalized to the latest millisecond on its year/month/day.
     */
    public static Calendar latestDayMillis( Calendar cal ) {
        cal.set( Calendar.HOUR_OF_DAY, 23 );
        cal.set( Calendar.MINUTE, 59 );
        cal.set( Calendar.SECOND, 59 );
        cal.set( Calendar.MILLISECOND, 999 );
        return cal;
    }


    public static Date fromString( String pattern, SimpleDateFormat sdf ) {
        synchronized ( sdf ) {
            try {
                return sdf.parse(pattern);
            } catch (ParseException e) {
                return null;
            }
        }
    }

    public static String format( DateFormat df, Date date ) {
        synchronized ( df ) {
            return df.format( date );
        }
    }

    public static Date parse( DateFormat df, String source ) {
        synchronized ( df ) {
            try {
                return df.parse( source );
            } catch (ParseException e) {
                return null;
            }
        }
    }

}
